
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;
using Swaf;

[assembly:SecurityPermissionAttribute(SecurityAction.RequestMinimum, UnmanagedCode=true)]
[assembly:PermissionSetAttribute(SecurityAction.RequestMinimum, Name = "FullTrust")]
namespace Swaf.Security
{
	public class Impersonation
	{
		[DllImport("advapi32.dll", SetLastError=true)]
		public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, 
			int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

		[DllImport("kernel32.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)]
		private unsafe static extern int FormatMessage(int dwFlags, ref IntPtr lpSource, 
			int dwMessageId, int dwLanguageId, ref String lpBuffer, int nSize, IntPtr *Arguments);

		[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
		public extern static bool CloseHandle(IntPtr handle);

		[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, 
			int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);

		WindowsImpersonationContext impersonatedUser;
		IntPtr tokenHandle = new IntPtr(0);
		IntPtr dupeTokenHandle = new IntPtr(0);

		// GetErrorMessage formats and returns an error message
		// corresponding to the input errorCode.
		public unsafe static string GetErrorMessage(int errorCode)
		{
			int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
			int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
			int FORMAT_MESSAGE_FROM_SYSTEM  = 0x00001000;

			//int errorCode = 0x5; //ERROR_ACCESS_DENIED
			//throw new System.ComponentModel.Win32Exception(errorCode);

			int messageSize = 255;
			String lpMsgBuf = "";
			int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;

			IntPtr ptrlpSource = IntPtr.Zero;
			IntPtr prtArguments = IntPtr.Zero;
        
			int retVal = FormatMessage(dwFlags, ref ptrlpSource, errorCode, 0, ref lpMsgBuf, messageSize, &prtArguments);
			if (0 == retVal)
			{
				throw new Exception("Failed to format message for error code " + errorCode + ". ");
			}

			return lpMsgBuf;
		}

		// Test harness.
		// If you incorporate this code into a DLL, be sure to demand FullTrust.
		[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
		public void ImpersonateUser(string Domain, string UserId, string Password)
		{    
			try
			{
				const int LOGON32_PROVIDER_DEFAULT = 0;
				//This parameter causes LogonUser to create a primary token.
				const int LOGON32_LOGON_INTERACTIVE = 2;
				const int SecurityImpersonation = 2;

				tokenHandle = IntPtr.Zero;
				dupeTokenHandle = IntPtr.Zero;

				// Call LogonUser to obtain a handle to an access token.
				bool returnValue = LogonUser(UserId, Domain, Password, 
					LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
					ref tokenHandle);
                    
				if (false == returnValue)
				{
					int ret = Marshal.GetLastWin32Error();
					Application.currentApp.Log.println("LogonUser failed with error code : {0}", ret);
					Application.currentApp.Log.println("\nError: [{0}] {1}\n", ret, GetErrorMessage(ret));
					int errorCode = 0x5; //ERROR_ACCESS_DENIED
					throw new System.ComponentModel.Win32Exception(errorCode);
				}

				bool retVal = DuplicateToken(tokenHandle, SecurityImpersonation, ref dupeTokenHandle);
				if (false == retVal)
				{
					CloseHandle(tokenHandle);
					Application.currentApp.Log.println("Exception thrown in trying to duplicate token.");        
					throw new Exception("Exception trying to duplicate token");
					return;
				}
            
				// The token that is passed to the following constructor must 
				// be a primary token in order to use it for impersonation.
				WindowsIdentity newId = new WindowsIdentity(dupeTokenHandle);
				impersonatedUser = newId.Impersonate();
            
			}
			catch(Exception ex)
			{
				Console.WriteLine("Exception occurred. " + ex.Message);
			}

		}
		public void stopImpersonation()
		{
			// Stop impersonating the user.
			impersonatedUser.Undo();

			// Free the tokens.
			if (tokenHandle != IntPtr.Zero)
				CloseHandle(tokenHandle);
			if (dupeTokenHandle != IntPtr.Zero) 
				CloseHandle(dupeTokenHandle);

		}
	}
}